#include "stdafx.h"
#include "CppUnitTest.h"

#include <vex/iterable/make_any_range.h>
#include <vex.tests/mstest/tests.range/tests.range.h>

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace tests {	namespace range_any_stl_string {

    using namespace boost::assign;

    namespace vi = vex::iterable;

    TEST_CLASS(range_stl_string)
    {
        BEGIN_TEST_CLASS_ATTRIBUTE()
            TEST_CLASS_ATTRIBUTE(L"Module", L"tests.range.any.stl.string")
            END_TEST_CLASS_ATTRIBUTE();

        static const std::size_t m_container_size = 4u;
        typedef std::string container_t;
        typedef container_t::iterator iterator_t;
        typedef container_t::const_iterator const_iterator_t;

        container_t 
            m_container, 
            m_container_same_size_different_values, 
            m_container_empty, 
            m_container_with_one_element;
    public:
        TEST_METHOD_INITIALIZE(set_up)
        {
            container_t("1234").swap(m_container);
            container_t("5678").swap(m_container_same_size_different_values);
            container_t().swap(m_container_empty);
            container_t("1").swap(m_container_with_one_element);
        }

        TEST_METHOD_CLEANUP(tear_down)
        {
            container_t().swap(m_container);
            container_t().swap(m_container_same_size_different_values);
            container_t().swap(m_container_empty);
            container_t().swap(m_container_with_one_element);
        }

        TEST_METHOD(concepts_and_traits_are_satisfied)
        {
            typedef decltype(vi::make_any_range(m_container)) range_t;
            typedef vi::range_traits<range_t> traits_t;

            BOOST_CONCEPT_ASSERT((vi::concept::RandomAccessRange<range_t>));
            Assert::IsTrue(std::is_same<container_t::reference, traits_t::reference>::value);
            Assert::IsTrue(std::is_same<container_t::value_type, traits_t::value_type>::value);
            Assert::IsTrue(std::is_same<container_t::pointer, traits_t::pointer>::value);
        }

        TEST_METHOD(supports_move)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();

            auto t_r1 = vi::make_any_range(m_container);
            auto t_r2 = std::move(t_r1);
            
            Assert::IsFalse(t_r1);
            Assert::IsNull(t_r1.get());
            Assert::IsTrue(t_r2);
            Assert::IsNotNull(t_r2.get());
        }

        TEST_METHOD(supports_swap)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();

            auto t_r1 = vi::make_any_range(m_container);
            auto t_r2 = vi::make_any_range(m_container_same_size_different_values);

            random_access_range::supports_and_executes_swap_correctly(
                m_container, m_container_same_size_different_values, t_r1, t_r2);
        }

        TEST_METHOD(supports_non_const_to_const_conversion_but_not_vice_versa)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();

            typedef decltype(vi::make_any_range(
                std::declval<iterator_t>(), std::declval<iterator_t>())) range_t;
            typedef decltype(vi::make_any_range(
                std::declval<const_iterator_t>(), std::declval<const_iterator_t>())) const_range_t;

            Assert::IsTrue(std::is_convertible<range_t, const_range_t>::value);
            Assert::IsFalse(std::is_convertible<const_range_t, range_t>::value);
        }

        TEST_METHOD(move_front_iteration_succeeds_expected_number_of_times)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();

            random_access_range::move_front_iteration_succeeds_expected_number_of_times(
                vi::make_any_range(m_container_empty), m_container_empty.size());

            random_access_range::move_front_iteration_succeeds_expected_number_of_times(
                vi::make_any_range(m_container), m_container.size());

            random_access_range::move_front_iteration_succeeds_expected_number_of_times(
                vi::make_any_range(m_container.begin(), m_container.end()), m_container.size());

            random_access_range::move_front_iteration_succeeds_expected_number_of_times(
                vi::make_any_range(m_container.cbegin(), m_container.cend()), m_container.size());
        }

        TEST_METHOD(each_subsequent_move_front_or_move_back_decreases_size_by_one)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();

            random_access_range::size_always_has_expected_value(
                vi::make_any_range(m_container), m_container.size());

            random_access_range::each_subsequent_move_front_decreases_size_by_one(
                vi::make_any_range(m_container));

            random_access_range::each_subsequent_move_back_decreases_size_by_one(
                vi::make_any_range(m_container));
        }

        TEST_METHOD(each_subsequent_move_front_of_or_move_back_of_decreases_size_by_one)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();

            random_access_range::size_always_has_expected_value(
                vi::make_any_range(m_container), m_container.size());

            random_access_range::each_subsequent_move_front_of_decreases_size_by_one(
                vi::make_any_range(m_container));

            random_access_range::each_subsequent_move_back_of_decreases_size_by_one(
                vi::make_any_range(m_container));
        }

        TEST_METHOD(move_front_succeeds_n_times_on_range_with_n_elements)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            random_access_range::move_front_succeeds_n_times_on_range_with_n_elements(
                vi::make_any_range(m_container), m_container.cbegin(), m_container.cend());

            random_access_range::move_front_succeeds_n_times_on_range_with_n_elements(
                vi::make_any_range(m_container_empty), m_container_empty.cbegin(), m_container_empty.cend());
        }

        TEST_METHOD(after_calling_move_front_n_times_front_points_to_nth_element)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            for (auto t_size = m_container.size(); t_size > 0; --t_size) {
                random_access_range::after_calling_move_front_n_times_front_points_to_nth_element(
                    vi::make_any_range(m_container), t_size, m_container.cbegin(), m_container.cend());
            }
        }

        // forward iterable portion
        TEST_METHOD(save_creates_a_copy_of_range)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            forward_range::save_creates_a_copy_of_current_range(vi::make_any_range(m_container));
            forward_range::save_creates_a_copy_of_current_range(vi::make_any_range(m_container_with_one_element));
            forward_range::save_creates_a_copy_of_current_range(vi::make_any_range(m_container_empty));
        }

        TEST_METHOD(move_back_succeeds_n_times_on_range_with_n_elements)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            random_access_range::move_back_succeeds_n_times_on_range_with_n_elements(
                vi::make_any_range(m_container), m_container.crbegin(), m_container.crend());

            random_access_range::move_back_succeeds_n_times_on_range_with_n_elements(
                vi::make_any_range(m_container_empty), m_container_empty.crbegin(), m_container_empty.crend());
        }

        TEST_METHOD(after_calling_move_back_n_times_back_points_to_nth_element_from_back)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            for (auto t_size = m_container.size(); t_size > 0; --t_size) {
                random_access_range::after_calling_move_back_n_times_back_points_to_nth_element_from_back(
                    vi::make_any_range(m_container), t_size, m_container.crbegin(), m_container.crend());
            }
        }

        TEST_METHOD(either_move_front_or_move_back_are_possible_on_range_with_one_element)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            random_access_range::either_move_front_or_move_back_are_possible_on_range_with_one_element(
                vi::make_any_range(m_container_with_one_element));
        }

        TEST_METHOD(front_and_back_are_equal_on_range_with_one_element)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            random_access_range::front_and_back_are_equal_on_range_with_one_element(
                vi::make_any_range(m_container_with_one_element), m_container_with_one_element.front());
        }

        
        TEST_METHOD(at_and_size_are_available_on_not_started_range)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            {
                std::vector<std::size_t> t_indices;
                t_indices += 0u, 1u, 2u, 3u;
                random_access_range::at_and_size_are_available_on_not_started_range(
                    vi::make_any_range(m_container), t_indices.begin(), t_indices.end(), m_container.begin());        
            }

            {
                std::vector<std::size_t> t_indices;
                t_indices += 0u;
                random_access_range::at_and_size_are_available_on_not_started_range(
                    vi::make_any_range(m_container_with_one_element), t_indices.begin(), t_indices.end(), m_container_with_one_element.begin());
            }
        }

        TEST_METHOD(front_and_back_meet_in_middle_of_range_with_n_elements)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            bidirectional_range::front_and_back_meet_in_middle_of_range_with_n_elements(
                vi::make_any_range(m_container),
                m_container.begin(), 
                m_container.end());

            bidirectional_range::front_and_back_meet_in_middle_of_range_with_n_elements(
                vi::make_any_range(m_container_with_one_element), 
                m_container_with_one_element.begin(), 
                m_container_with_one_element.end());
        }


        TEST_METHOD(front_always_equals_at_of_zero_in_non_empty_range)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            random_access_range::front_always_equals_at_of_zero_in_non_empty_range(
                vi::make_any_range(m_container));

            random_access_range::front_always_equals_at_of_zero_in_non_empty_range(
                vi::make_any_range(m_container_with_one_element));

            random_access_range::front_always_equals_at_of_zero_in_non_empty_range(
                vi::make_any_range(m_container_empty));
        }

        TEST_METHOD(back_always_equals_at_of_size_minus_one_in_non_empty_range)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            random_access_range::back_always_equals_at_of_size_minus_one_in_non_empty_range(
                vi::make_any_range(m_container));

            random_access_range::back_always_equals_at_of_size_minus_one_in_non_empty_range(
                vi::make_any_range(m_container_with_one_element));

            random_access_range::back_always_equals_at_of_size_minus_one_in_non_empty_range(
                vi::make_any_range(m_container_empty));
        }

        TEST_METHOD(effects_of_move_front_of)
        {
            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                random_access_range::move_front_of_n_has_the_same_effect_as_calling_move_front_n_times(
                    vi::make_any_range(m_container), 1u);
                random_access_range::move_front_of_n_has_the_same_effect_as_calling_move_front_n_times(
                    vi::make_any_range(m_container), 2u);
                random_access_range::move_front_of_n_has_the_same_effect_as_calling_move_front_n_times(
                    vi::make_any_range(m_container), 3u);
            }

            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                random_access_range::move_front_of_n_returns_false_for_empty_range_and_arbitrary_n(
                    vi::make_any_range(m_container_empty), 0u);
                random_access_range::move_front_of_n_returns_false_for_empty_range_and_arbitrary_n(
                    vi::make_any_range(m_container_empty), 1u);
                random_access_range::move_front_of_n_returns_false_for_empty_range_and_arbitrary_n(
                    vi::make_any_range(m_container_empty), std::numeric_limits<std::size_t>::max());
            }

            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                random_access_range::move_front_of_n_returns_false_for_n_equal_0_if_range_is_not_started(
                    vi::make_any_range(m_container_empty));
                random_access_range::move_front_of_n_returns_false_for_n_equal_0_if_range_is_not_started(
                    vi::make_any_range(m_container_empty));
                random_access_range::move_front_of_n_returns_false_for_n_equal_0_if_range_is_not_started(
                    vi::make_any_range(m_container_empty));
            }

            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                random_access_range::move_front_of_n_returns_false_for_n_out_of_bounds(
                    vi::make_any_range(m_container));
                random_access_range::move_front_of_n_returns_false_for_n_out_of_bounds(
                    vi::make_any_range(m_container_with_one_element));
                random_access_range::move_front_of_n_returns_false_for_n_out_of_bounds(
                    vi::make_any_range(m_container_empty));
            }

            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                random_access_range::move_front_of_n_returns_true_if_n_is_size_and_then_front_is_back_and_size_is_one(
                    vi::make_any_range(m_container));
                random_access_range::move_front_of_n_returns_true_if_n_is_size_and_then_front_is_back_and_size_is_one(
                    vi::make_any_range(m_container_with_one_element));
            }
        }

        TEST_METHOD(effects_of_move_back_of)
        {
            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                random_access_range::move_back_of_n_has_the_same_effect_as_calling_move_back_n_times(
                    vi::make_any_range(m_container), 1u);
                random_access_range::move_back_of_n_has_the_same_effect_as_calling_move_back_n_times(
                    vi::make_any_range(m_container), 2u);
                random_access_range::move_back_of_n_has_the_same_effect_as_calling_move_back_n_times(
                    vi::make_any_range(m_container), 3u);
            }

            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                random_access_range::move_back_of_n_returns_false_for_empty_range_and_arbitrary_n(
                    vi::make_any_range(m_container_empty), 0u);
                random_access_range::move_back_of_n_returns_false_for_empty_range_and_arbitrary_n(
                    vi::make_any_range(m_container_empty), 1u);
                random_access_range::move_back_of_n_returns_false_for_empty_range_and_arbitrary_n(
                    vi::make_any_range(m_container_empty), std::numeric_limits<std::size_t>::max());
            }

            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                random_access_range::move_back_of_n_returns_false_for_n_equal_0_if_range_is_not_started(
                    vi::make_any_range(m_container_empty));
                random_access_range::move_back_of_n_returns_false_for_n_equal_0_if_range_is_not_started(
                    vi::make_any_range(m_container_empty));
                random_access_range::move_back_of_n_returns_false_for_n_equal_0_if_range_is_not_started(
                    vi::make_any_range(m_container_empty));
            }

            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                random_access_range::move_back_of_n_returns_false_for_n_out_of_bounds(
                    vi::make_any_range(m_container));
                random_access_range::move_back_of_n_returns_false_for_n_out_of_bounds(
                    vi::make_any_range(m_container_with_one_element));
                random_access_range::move_back_of_n_returns_false_for_n_out_of_bounds(
                    vi::make_any_range(m_container_empty));
            }

            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                random_access_range::move_back_of_n_returns_true_if_n_is_size_and_then_back_is_front_and_size_is_one(
                    vi::make_any_range(m_container));
                random_access_range::move_back_of_n_returns_true_if_n_is_size_and_then_back_is_front_and_size_is_one(
                    vi::make_any_range(m_container_with_one_element));
            }
        }
    };
}}